package linmath

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"math"
	"src/protoMsg"

	"github.com/arl/math32"
)

// Vector3 代码位置的3D矢量
type Vector3 struct {
	protoMsg.Vector3
}

//--------------------------------------------
//db接口

func (this *Vector3) Scan(value interface{}) error {
	if value == nil {
		return nil
	} else if bli, ok := value.([]byte); ok {
		return this.Unmarshal(bli)
	}
	return errors.New("destination pointer is nil") // embedded in descriptive error

}

func (this Vector3) Value() (driver.Value, error) {
	return this.Marshal()
}

func (this *Vector3) RedisValue() (driver.Value, error) {
	return this.Marshal()
}

func (this Vector3) String() string {
	return fmt.Sprintf("x=%f,y=%f,z=%f", this.X, this.Y, this.Z)
}

//db接口
//--------------------------------------------

func NewVector3Imsg(v protoMsg.Vector3) Vector3 {
	return Vector3{
		Vector3: v,
	}
}

// NewVector3 创建一个新的矢量
func NewVector3(x, y, z float32) Vector3 {
	return Vector3{
		Vector3: protoMsg.Vector3{
			X: x,
			Y: y,
			Z: z,
		},
	}
}

var (
	VECTOR3_ZERO Vector3 = Vector3{
		Vector3: protoMsg.Vector3{
			X: 0,
			Y: 0,
			Z: 0,
		},
	}
)

// Vector3_Zero 返回零值
func Vector3_Zero() *Vector3 {
	return &VECTOR3_ZERO
}

var (
	VECTOR3_INVALID Vector3 = Vector3{
		Vector3: protoMsg.Vector3{
			X: math.MaxFloat32,
			Y: math.MaxFloat32,
			Z: math.MaxFloat32,
		},
	}
)

// Vector3_Invalid 返加一个无效的值 ，未赋值之前
func Vector3_Invalid() *Vector3 {
	return &VECTOR3_INVALID
}

// IsInValid 是否有效
func (v Vector3) IsInValid() bool {
	return v.IsEqual(*Vector3_Invalid())
}

func (v *Vector3) InValid() {
	v.X = math.MaxFloat32
	v.Y = math.MaxFloat32
	v.Z = math.MaxFloat32
}

// IsEqual 相等
func (v Vector3) IsEqual(r Vector3) bool {
	if v.X-r.X > math.SmallestNonzeroFloat32 ||
		v.X-r.X < -math.SmallestNonzeroFloat32 ||
		v.Y-r.Y > math.SmallestNonzeroFloat32 ||
		v.Y-r.Y < -math.SmallestNonzeroFloat32 ||
		v.Z-r.Z > math.SmallestNonzeroFloat32 ||
		v.Z-r.Z < -math.SmallestNonzeroFloat32 {
		return false
	}

	return true
}

// IsEqualXYZ
func (v Vector3) IsEqualXYZ(r Vector3) bool {
	return math32.Approx(v.X, r.X) &&
		math32.Approx(v.Y, r.Y) &&
		math32.Approx(v.Z, r.Z)
}

// IsEqualXZ 不判断高度
func (v Vector3) IsEqualXZ(r Vector3) bool {
	return int(v.X*100) == int(r.X*100) &&
		int(v.Z*100) == int(r.Z*100)
	// return math32.Approx(v.X, r.X) &&
	// 	math32.Approx(v.Z, r.Z)
}

// Add 加
func (v Vector3) Add(o Vector3) Vector3 {
	return Vector3{
		Vector3: protoMsg.Vector3{
			X: v.X + o.X,
			Y: v.Y + o.Y,
			Z: v.Z + o.Z,
		},
	}
}

// AddS 加到自己身上
func (v *Vector3) AddS(o Vector3) {
	v.X += o.X
	v.Y += o.Y
	v.Z += o.Z
}

// Sub 减
func (v Vector3) Sub(o Vector3) Vector3 {
	return Vector3{
		Vector3: protoMsg.Vector3{
			X: v.X - o.X,
			Y: v.Y - o.Y,
			Z: v.Z - o.Z,
		},
	}
}

// SubS 自已身上减
func (v *Vector3) SubS(o Vector3) {
	v.X -= o.X
	v.Y -= o.Y
	v.Z -= o.Z
}

// Mul 乘
func (v Vector3) Mul(o float32) Vector3 {
	return Vector3{
		Vector3: protoMsg.Vector3{
			X: v.X * o,
			Y: v.Y * o,
			Z: v.Z * o,
		},
	}
}

// MulS 自己乘
func (v *Vector3) MulS(o float32) {
	v.X *= o
	v.Y *= o
	v.Z *= o
}

// Cross 叉乘
func (v Vector3) Cross(o Vector3) Vector3 {
	return Vector3{
		Vector3: protoMsg.Vector3{
			X: v.Y*o.Z - v.Z*o.Y,
			Y: v.Z*o.X - v.X*o.Z,
			Z: v.X*o.Y - v.Y*o.X,
		},
	}
}

// Dot 点乘
func (v Vector3) Dot(o Vector3) float32 {
	return v.X*o.X + v.Y*o.Y + v.Z*o.Z
}

func (v Vector3) Dot2(o Vector3) float32 {
	return v.X*o.X + v.Z*o.Z
}

func (v Vector3) Sqr() float32 {
	return v.X*v.X + v.Z*v.Z
}

// Len 获取长度
// 这个函数已经不考虑Y轴方向的问题；
func (v Vector3) Len() float32 {
	return float32(math.Sqrt(float64(v.LenPow2())))
}

// Dot 点乘，不在考虑Y轴；
func (v Vector3) LenPow2() float32 {
	return v.X*v.X + v.Z*v.Z
}

// 超出范围
// Y轴方向已忽略，而且无开方
func (v Vector3) IsOutOfRange(r float32) bool {
	return v.LenPow2() > NewVector3(r, 0, 0).LenPow2()
}

// 超出范围
// Y轴方向已忽略，而且无开方
func (v Vector3) IsOutOfRangeInt32(r int32) bool {
	return v.IsOutOfRange(float32(r))
}

func (v *Vector3) Normalize() {
	len := v.Len()

	if len < math.SmallestNonzeroFloat32 {
		return
	}

	v.X = v.X / len
	v.Y = v.Y / len
	v.Z = v.Z / len
}
